home *** CD-ROM | disk | FTP | other *** search
- #include "GXExceptions.h"
- #include "GXGraphics.h"
- #include "ComputeLength.h"
- #include "PathWalking.h"
- #include "AccurateShapes.h"
-
- /********************************
-
- ApplyOneBendDash:
-
- This is my own version of PrimitiveShape for the case of
- a bend dash where the dash shape is a polygon. (see rules below)
-
- This is because there seem to be inaccuracies in GX dashing for this
- case which cause the text positioning to look bad when the dashing
- is used to determine CurveLayout.
-
- Since We know the advance is zero, we only have to repeat the dash once.
- This makes it easy.
-
-
- ********************************/
-
- typedef struct {
-
- gxPoint currentPoint; // the current point in our state.
- gxPoint firstPoint; // the first point in the current contour.
- double currentLength; // cache of current length;
- long currentDashSegment; // index of current dash shape segment.
- long dashSegmentCount; // How many segmentsin the dash shape.
- double *startLengths; // Pointer to array of dash segment start lengths;
- double *endLengths; // Pointer to array of dash segment end lengths;
- Boolean startedSegment; // Have we started building a bent segment?
- gxShape bendDashShape; // Build the bend dash in here.
-
- } TBendDashRecord;
-
-
- void CurveToPathsStructure(gxCurve *subCurve, gxPaths *subPath);
- void CurveToPathsStructure(gxCurve *subCurve, gxPaths *subPath)
- {
- char *pWalker = (char*)subPath;
- long *aLong;
- gxPoint *aPoint;
-
- aLong = (long*)pWalker;
- pWalker += sizeof(long);
- *aLong = 1; // how many countours.
-
- aLong = (long*)pWalker;
- pWalker += sizeof(long);
- *aLong = 3; // how many poins in the contour.
-
- aLong = (long*)pWalker;
- pWalker += sizeof(long);
- *aLong = 0x40000000; // control bits: 0-1-0-... for on off on.
-
- /* now the points */
-
- aPoint = (gxPoint*)pWalker;
- aPoint->x = subCurve->first.x;
- aPoint->y = subCurve->first.y;
- aPoint += 1;
-
- aPoint->x = subCurve->control.x;
- aPoint->y = subCurve->control.y;
- aPoint += 1;
-
- aPoint->x = subCurve->last.x;
- aPoint->y = subCurve->last.y;
-
- }//CurveToPathsStructure
-
-
- /*******
-
- PathWalker Callbacks for implementing bend dashing of hairlines:
-
- As we enter a line or a curve we look at the current dash segment's
- start and end length. This information determines whether the current
- shape's curve or line segment contains the dash. It can contain all or
- part of the dash. In the case where it contains part of the dash, the part
- is computed and added to the bendDash result.
-
- ********/
-
-
- Boolean BendDashCurveto(gxPoint p[3], TBendDashRecord *bdr);
- Boolean BendDashCurveto(gxPoint p[3], TBendDashRecord *bdr)
- {
- gxCurve aCurve, subCurve;
- char subPathData[ sizeof(long) + sizeof(long) + 3 * sizeof(gxPoint) + sizeof(long)];
- gxPaths *subPath = (gxPaths*)subPathData;
- double segmentLength;
- double d1, d2;
- Boolean inSegment = false;
- Boolean startContour = false;
- Boolean result = false;
-
- aCurve.first.x = p[0].x;
- aCurve.first.y = p[0].y;
- aCurve.control.x = p[1].x;
- aCurve.control.y = p[1].y;
- aCurve.last.x = p[2].x;
- aCurve.last.y = p[2].y;
-
- segmentLength = GetCurveLength(&aCurve);
-
- if (!bdr->startedSegment) {
-
- // If we haven't started with this dash segment, see if we should start.
-
- if ( bdr->startLengths[bdr->currentDashSegment] <= (bdr->currentLength + segmentLength) ) {
- bdr->startedSegment = true;
- d1 = bdr->startLengths[bdr->currentDashSegment] - bdr->currentLength;
- inSegment = true;
- startContour = true;
- }//end if
-
- }//end if
-
- // compute the end length of the segment. If the contour surpasses the segment then end length is segment length
- if (bdr->startedSegment) {
-
- if (!inSegment) d1 = 0.0;
- if (bdr->endLengths[bdr->currentDashSegment] <= (bdr->currentLength + segmentLength) )
- d2 = bdr->endLengths[bdr->currentDashSegment] - bdr->currentLength;
- else
- d2 = segmentLength;
-
- inSegment = true;
-
- }//end if
-
- if (inSegment) {
-
- // now compute the subcurve from distances d1 through d2.
- DetermineSubCurve(&aCurve, d1, d2, &subCurve);
- CurveToPathsStructure(&subCurve, subPath);
-
- // Now add the subcurve to the dashed shape we are building.
- if (startContour)
- GXSetPathParts(bdr->bendDashShape, 0, 0, subPath, gxBreakLeftEdit);
- else
- GXSetPathParts(bdr->bendDashShape, 0, 0, subPath, gxBreakNeitherEdit);
-
- // now see if we should go to the next contour of the dash shape or we're done.
- if (bdr->endLengths[bdr->currentDashSegment] <= (bdr->currentLength + segmentLength) ) {
-
- bdr->startedSegment = false;
- bdr->currentDashSegment += 1;
- if (bdr->currentDashSegment >= bdr->dashSegmentCount) // we've exausted dash segments.
- result = true;
-
- }//end if
-
- }//endif
-
- /* update our state */
-
- bdr->currentPoint.x = aCurve.last.x;
- bdr->currentPoint.y = aCurve.last.y;
- bdr->currentLength += segmentLength;
-
- return(result);
-
- }//BendDahsCurveto
-
-
- Boolean BendDashLineto(gxPoint *p, TBendDashRecord *bdr);
- Boolean BendDashLineto(gxPoint *p, TBendDashRecord *bdr)
- {
- gxLine aLine;
- Boolean stop;
-
- aLine.first.x = bdr->currentPoint.x;
- aLine.first.y = bdr->currentPoint.y;
- aLine.last.x = p->x;
- aLine.last.y = p->y;
-
- /* For now, treat the line like a curve so we share the code, not as efficient, but simpler */
- {
- gxPoint curvep[3];
- curvep[0].x = aLine.first.x;
- curvep[0].y = aLine.first.y;
-
- curvep[1].x = (aLine.first.x + aLine.last.x) / 2;
- curvep[1].y = (aLine.first.y + aLine.last.y) / 2;
-
- curvep[2].x = aLine.last.x;
- curvep[2].y = aLine.last.y;
-
- stop = BendDashCurveto(curvep, bdr);
-
- }
-
- bdr->currentPoint.x = p->x;
- bdr->currentPoint.y = p->y;
-
- return(stop);
-
- }//BendDashLineto
-
-
- Boolean BendDashClosepath(TBendDashRecord *bdr);
- Boolean BendDashClosepath(TBendDashRecord *bdr)
- {
- Boolean stop;
- stop = BendDashLineto(&(bdr->firstPoint), bdr); // just do a line from current to first.
- return(stop);
-
- }//BendDashClosepath
-
- Boolean BendDashMoveto(gxPoint *p, TBendDashRecord *bdr);
- Boolean BendDashMoveto(gxPoint *p, TBendDashRecord *bdr)
- {
- bdr->currentPoint.x = p->x;
- bdr->currentPoint.y = p->y;
- bdr->firstPoint.x = p->x;
- bdr->firstPoint.y = p->y;
- bdr->currentLength = 0.0;
- bdr->currentDashSegment = 0;
- bdr->startedSegment = false;
-
- return(false);
-
- }//BendDashMoveto
-
-
- /****
- Client routine for PathWalker library that will do bend dashing
- *****/
- void ApplyOneBendDash(gxShape theShape, gxDashRecord *theDash);
- void ApplyOneBendDash(gxShape theShape, gxDashRecord *theDash)
- {
- TBendDashRecord bdr;
- long idx;
- gxRectangle dashSegRect;
-
- bdr.bendDashShape = GXNewShape(gxPathType); // The result will always be a path.
-
- // allocate memory for length arrays; do it with one allocation split in half.
- bdr.dashSegmentCount = GXCountShapeContours(theDash->dash);
- bdr.startLengths = (double*)NewPtr(2 * bdr.dashSegmentCount * sizeof(double));
- bdr.endLengths = bdr.startLengths + bdr.dashSegmentCount;
- require_action(bdr.startLengths, failed_alloc1, GXPostGraphicsError(out_of_memory););
-
- // Load length arrays. values based on horizontal bounds of contour.
- for (idx = 0; idx < bdr.dashSegmentCount; ++idx) {
-
- GXGetShapeBounds(theDash->dash, idx + 1, &dashSegRect);
- bdr.startLengths[idx] = FixedToDouble(dashSegRect.left);
- bdr.endLengths[idx] = FixedToDouble(dashSegRect.right);
-
- }//end for
-
- // Go for it.
- (void)ShapeWalker( theShape, BendDashMoveto, BendDashLineto, BendDashCurveto, BendDashClosepath, &bdr);
-
- // dispose of allocations.
- DisposePtr((Ptr)bdr.startLengths);
-
- // Move the bent shape into target.
- GXCopyToShape(theShape, bdr.bendDashShape);
- GXSetShapeFill(theShape, gxFrameFill); // Bend dashing is always opened frame.
-
- failed_alloc1:
-
- GXDisposeShape(bdr.bendDashShape);
-
- }//ApplyOneBendDash
-
-
-
-
-
- /********************************
-
- ApplyOneBreakDash:
-
- This is my own version of PrimitiveShape for the case of
- a break dash where the dash shape is a polygon. (see rules below)
-
- This is because there seem to be inaccuracies in GX dashing for this
- case which cause the text positioning to look bad when the dashing
- is used to determine CurveLayout.
-
- Since We know the advance is zero, we only have to repeat the dash once.
- This makes it easy.
-
- ********************************/
- void ApplyOneBreakDash(gxShape theShape, gxDashRecord *theDash);
- void ApplyOneBreakDash(gxShape theShape, gxDashRecord *theDash)
- {
- Ptr polyWalker;
- long numContours;
- long pointsInContour;
- long idx;
- gxPoint contourCenter;
- Fixed shapeLength;
- Fixed distance;
- gxPoint location, tangent;
- Fixed pen;
- gxMapping contourMapping, aMapping;
- wide wLength;
-
- AccurateGetShapeLength(theShape, 0, &wLength);
- shapeLength = (Fixed)wLength.lo;
-
- pen = ff(1); //GXGetShapePen(theShape);
-
- GXLockShape(theDash->dash);
- polyWalker = GXGetShapeStructure(theDash->dash, nil);
-
- numContours = *(long*)polyWalker;
- polyWalker += sizeof(long);
-
- /******************************
-
- For each contour, find the tangent point on the curve at
- the distance on the curve corresponding to the horizontal center
- of the contour.
- Each contour is scaled by the pen thickness about it's horizontal center.
- Each scaled contour is rotated about its horizontal center (x,0) by
- at an angle determined by the tangent vector to the curve.
- Each rotated contour is then moved so its horizontal position (x,0)
- is at the point along the curve.
-
- ******************************/
- for (idx = 1; idx <= numContours; ++idx) {
-
- pointsInContour = *(long*)polyWalker;
- polyWalker += sizeof(long);
-
- GXGetShapeCenter(theDash->dash, idx, &contourCenter);
-
- /***
- find out the distance into the curve to put this countour,
- wrap if necessary
- ***/
- distance = contourCenter.x;
- if (distance > shapeLength)
- distance = distance % (shapeLength + 0x00000001);
-
- AccurateShapeLengthToPoint(theShape, 0, distance, &location, &tangent);
-
- /* Make a mapping for the contour based on the tangent information */
-
- ResetMapping(&contourMapping);
-
- /* Move Mapping to origin is at horizontal center of cotour */
-
- MoveMapping(&contourMapping, -contourCenter.x, 0);
-
- /* Scale the Mapping by the pen in the Y direction */
-
- //ScaleMapping(&contourMapping, 0, pen, -contourCenter.x, 0);
-
- /* Now rotate the mapping by the tangent vector */
- ResetMapping(&aMapping);
- aMapping.map[0][0] = tangent.x;
- aMapping.map[1][0] = -tangent.y;
- aMapping.map[0][1] = tangent.y;
- aMapping.map[1][1] = tangent.x;
- MapMapping(&contourMapping, &aMapping);
- MoveMapping(&contourMapping, contourCenter.x, 0); // move back to original locaton
-
- /* Now translate mapping so contours horizontal center will be at point on curve */
-
- MoveMapping(&contourMapping, location.x - contourCenter.x, location.y );
-
- /* Map the points in the contour */
-
- MapPoints(&contourMapping, pointsInContour, (gxPoint*)polyWalker);
-
- /* Move walker to next contour */
-
- polyWalker += pointsInContour * sizeof(gxPoint);
-
- }//end for
-
- GXUnlockShape(theDash->dash);
-
- /* The dash now represents the input shape dashed with it, so copy the result into the input shape */
-
- GXCopyToShape(theShape, theDash->dash);
-
- return;
-
- }//ApplyOneBreakDash
-
-
- /****************************
-
- AccuratePrimitiveShape:
-
- This routine replaces GXPrimitiveShape. This
- is because of the inaccuracies in the GX internal
- dashing code which misplaces dash contours slightly,
- sometimes.
-
- It only works for the kind of dashing that
- we are using for finding CurveLayout glyph
- positions. (see below). The case is:
-
- • A framed shape with a dash
- • Dash is a polygon or glyph
- • Pen of 1.0 (or pen of zero with bend-dash)
- • Dash scale is 1.0
- • Dash advance is 0.0
- • Dash phase is 0.0
- • Break or bend dash attributes
-
-
- ****************************/
- void AccuratePrimitiveShape(gxShape theShape);
- void AccuratePrimitiveShape(gxShape theShape)
- {
- gxShapeFill theFill;
- gxDashRecord theDash;
- gxShapeType theType;
- Fixed thePen;
- Boolean useGXDashing = false;
-
- /* if it is not a framed shape, do normal primitve and return */
-
- theFill = GXGetShapeFill(theShape);
- if ( (theFill != gxClosedFrameFill) && (theFill != gxOpenFrameFill) ) {
-
- GXPrimitiveShape(theShape);
- return;
-
- }//end if
-
-
- /* if it does not have a dash do normal primitve and return */
-
- GXGetShapeDash(theShape, &theDash);
-
- if (theDash.dash == nil) {
-
- GXPrimitiveShape(theShape);
- return;
-
- }//end if
-
- /* If the pen is not 1 and it isn't zero with bendashing, do normal primitive */
- thePen = GXGetShapePen(theShape);
-
- if ((thePen != ff(1) && ( (thePen != 0) && (theDash.attributes & gxBendDash) ) ) ) {
-
- GXPrimitiveShape(theShape);
- GXDisposeShape(theDash.dash);
- return;
-
- }//end if
-
- /* if the dash ins't a polygon do normal primitve and return */
- theType = GXGetShapeType(theDash.dash);
- if (theType != gxPolygonType) {
-
- GXPrimitiveShape(theShape);
- GXDisposeShape(theDash.dash);
- return;
-
- }//end if
-
- useGXDashing = false;
- if ( theDash.phase != 0 )
- useGXDashing = true;
-
- if ( theDash.advance != 0)
- useGXDashing = true;
-
- if ( theDash.scale != ff(1) )
- useGXDashing = true;
-
- if ( !(theDash.attributes & gxBreakDash ) && !(theDash.attributes & gxBendDash))
- useGXDashing = true;
-
- if (useGXDashing) {
-
- GXPrimitiveShape(theShape);
- GXDisposeShape(theDash.dash);
- return;
-
- }//end if
-
- /* if we got to here, then call our dashing code */
-
- if ( theDash.attributes & gxBreakDash )
- ApplyOneBreakDash(theShape, &theDash);
- else if (theDash.attributes & gxBendDash )
- ApplyOneBendDash(theShape, &theDash);
-
-
- GXDisposeShape(theDash.dash);
-
- return;
-
- }//AccuratePrimitiveShape
-
-
-